package de.rankyman;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.List;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.OptionGroup;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.FilenameUtils;

import com.moviejukebox.thetvdb.TheTVDB;
import com.moviejukebox.thetvdb.model.Episode;
import com.moviejukebox.thetvdb.model.Series;

import de.rankyman.models.EpisodeFile;
import de.rankyman.tools.RegExMatcher;


public class Jtvr {
	final private static String apiKey = "1D727413E5343432";
	
	private static String[] inputFiles;
	private static String tvdbId = "";
	private static String seriesName = "";
	private static String language = "en";
	
	/**
	 * Apply Apache Commons CLI GnuParser to command-line arguments.
	 * 
	 * @param commandLineArguments
	 *            Command-line arguments to be processed with Gnu-style parser.
	 */
	public static void useGnuParser(final String[] commandLineArguments) {
		final CommandLineParser cmdLineGnuParser = new GnuParser();
		
		final Options gnuOptions = constructGnuOptions();
		CommandLine commandLine;
		try {
			commandLine = cmdLineGnuParser.parse(gnuOptions, commandLineArguments);
			
			if (commandLine.hasOption("s")) {
				seriesName = commandLine.getOptionValue("s");
				System.out.println("Search for: " + seriesName);
			}
			if (commandLine.hasOption("l")) {
				language = commandLine.getOptionValue("l");
				System.out.println("Language: " + language);
			}
			if (commandLine.hasOption("f")) {
				inputFiles = commandLine.getOptionValues('f');
//				for (String str : inputFiles) {
//					System.out.println("Input file: " + str);
//				}
			}
			if (commandLine.hasOption("i")) {
				tvdbId = commandLine.getOptionValue('i');
				System.out.println("TVDB ID: " + tvdbId);
			}
		} catch (ParseException parseException) // checked exception
		{
			System.err
					.println("Encountered exception while parsing using GnuParser:\n"
							+ parseException.getMessage());
		}
	}
	
	/**
	 * Construct and provide GNU-compatible Options.
	 * 
	 * @return Options expected from command-line of GNU form.
	 */
	@SuppressWarnings("static-access")
	public static Options constructGnuOptions() {
		final Options gnuOptions = new Options();
		final OptionGroup searchOrRename = new OptionGroup();
		
		//all options
		Option search   = new Option("s", "search",   true, "search for series");
		Option language = new Option("l", "language", true, "language");
		Option tvdbid   = new Option("i", "tvdbid",   true, "TVDB ID");
		Option files    = OptionBuilder
								.withLongOpt("files")
								.withDescription("files to rename")
								.hasArgs()
								.create("f");
		
		searchOrRename.addOption(search).addOption(tvdbid);
		
		gnuOptions.addOptionGroup(searchOrRename).addOption(language).addOption(files);
		
		return gnuOptions;
	}
	
	/**
	 * Display command-line arguments without processing them in any further
	 * way.
	 * 
	 * @param commandLineArguments
	 *            Command-line arguments to be displayed.
	 */
	public static void displayProvidedCommandLineArguments(
			final String[] commandLineArguments, final OutputStream out) {
		final StringBuffer buffer = new StringBuffer();
		for (final String argument : commandLineArguments) {
			buffer.append(argument).append(" ");
		} try {
			out.write((buffer.toString() + "\n").getBytes());
		} catch (IOException ioEx) {
			System.err
					.println("WARNING: Exception encountered trying to write to OutputStream:\n"
							+ ioEx.getMessage());
			System.out.println(buffer.toString());
		}
	}
	
	/**
	 * Display example application header.
	 * 
	 * @out OutputStream to which header should be written.
	 */
	public static void displayHeader(final OutputStream out) {
		final String header = "[Java TV-Show Renamer - jtvr]\n";
		try {
			out.write(header.getBytes());
		} catch (IOException ioEx) {
			System.out.println(header);
		}
	}
	
	/**
	 * Write the provided number of blank lines to the provided OutputStream.
	 * 
	 * @param numberBlankLines
	 *            Number of blank lines to write.
	 * @param out
	 *            OutputStream to which to write the blank lines.
	 */
	public static void displayBlankLines(final int numberBlankLines,
			final OutputStream out) {
		try {
			for (int i = 0; i < numberBlankLines; ++i) {
				out.write("\n".getBytes());
			}
		} catch (IOException ioEx) {
			for (int i = 0; i < numberBlankLines; ++i) {
				System.out.println();
			}
		}
	}
	
	/**
	 * Print usage information to provided OutputStream.
	 * 
	 * @param applicationName
	 *            Name of application to list in usage.
	 * @param options
	 *            Command-line options to be part of usage.
	 * @param out
	 *            OutputStream to which to write the usage information.
	 */
	public static void printUsage(
			final String applicationName,
			final Options options,
			final OutputStream out) {
		final PrintWriter writer = new PrintWriter(out);
		final HelpFormatter usageFormatter = new HelpFormatter();
		usageFormatter.printUsage(writer, 80, "java -jar " + applicationName + ".jar", options);
		writer.flush();
	}
	
	/**
	 * Write "help" to the provided OutputStream.
	 */
	public static void printHelp(
			final String applicationName,
			final Options options,
			final int printedRowWidth,
			final String header,
			final String footer,
			final int spacesBeforeOption,
			final int spacesBeforeOptionDescription,
			final boolean displayUsage,
			final OutputStream out) {
		final String commandLineSyntax = "java -jar " + applicationName + ".jar";
		final PrintWriter writer = new PrintWriter(out);
		final HelpFormatter helpFormatter = new HelpFormatter();
		helpFormatter.printHelp(
				writer,
				printedRowWidth,
				commandLineSyntax,
				header,
				options,
				spacesBeforeOption,
				spacesBeforeOptionDescription,
				footer,
				displayUsage);
		writer.flush();
	}
	
	
	
	public static void main(final String[] commandLineArguments) {
		final String applicationName = "jtvr";
		displayHeader(System.out);
		if (commandLineArguments.length < 1) {
//			printUsage(applicationName, constructGnuOptions(), System.out);
			Options gnuOptions = constructGnuOptions();
//			gnuOptions = constructGnuOptions();
			printHelp(applicationName, gnuOptions, 80, "", "", 5, 3, true, System.out);
		}
//		displayProvidedCommandLineArguments(commandLineArguments, System.out);
		useGnuParser(commandLineArguments);
		
		TheTVDB tvdb = new TheTVDB(apiKey);
		
		if (tvdbId == "") {
			if (seriesName != "") {
				List<Series> seriesList = tvdb.searchSeries(seriesName, language);
				
				System.out.println("Found ID's:");
				for (int i=0; i < seriesList.size(); i++) {
					System.out.println(
							"TVDB ID: " + seriesList.get(i).getId() +
							"\tNAME: "+ seriesList.get(i).getSeriesName());
				}
			}
		} else {
			RegExMatcher rxSeasonAndEpisode = new RegExMatcher(".*?[.\\s][sS](\\d{1,})[eE](\\d{1,}).*");
			
			Series series = tvdb.getSeries(tvdbId, language);
			
			for (String fileName : inputFiles) {
				List<String> lst = rxSeasonAndEpisode.match(fileName);
				
				String fileExtension = FilenameUtils.getExtension(fileName);
				
				Episode episode = tvdb.getEpisode(
						tvdbId,
						Integer.parseInt(lst.get(0)), //season,
						Integer.parseInt(lst.get(1)), //episode,
						language);
				
				String newFileName = String.format(
						"%s.S%02dE%02d.%s.%s",
						series.getSeriesName(),
						episode.getSeasonNumber(),
						episode.getEpisodeNumber(),
						episode.getEpisodeName(),
						fileExtension);
				
				EpisodeFile file = new EpisodeFile(fileName);
				file.setNewFileName(newFileName);
				System.out.println("Rename file: " + fileName);
				System.out.println("To:          " + newFileName);
				file.rename();
			}
		}
	}
}
